%% Copyright 2008 Kevin A. Smith 
%% Licensed under the Apache License, Version 2.0 (the "License"); 
%% you may not use this file except in compliance with the License. 
%% You may obtain a copy of the License at 
%%
%% http://www.apache.org/licenses/LICENSE-2.0 
%%
%% Unless required by applicable law or agreed to in writing, software 
%% distributed under the License is distributed on an "AS IS" BASIS, 
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
%% See the License for the specific language governing permissions and 
%% limitations under the License.

%% Many thanks to Bob Ippolito and the MochiWeb crew
%% for this incredibly useful code

-module(url_util).

-export([encode/1, escape/1]).

-define(PERCENT, 37).  % $\% 
-define(FULLSTOP, 46). % $\.
-define(IS_HEX(C), ((C >= $0 andalso C =< $9) orelse
		    (C >= $a andalso C =< $f) orelse
		    (C >= $A andalso C =< $F))).
-define(QS_SAFE(C), ((C >= $a andalso C =< $z) orelse
		     (C >= $A andalso C =< $Z) orelse
		     (C >= $0 andalso C =< $9) orelse
		     (C =:= ?FULLSTOP orelse C =:= $- orelse C =:= $~ orelse
		      C =:= $_))).

hexdigit(C) when C < 10 -> $0 + C;
hexdigit(C) when C < 16 -> $A + (C - 10).

escape(Atom) when is_atom(Atom) ->
    escape(atom_to_list(Atom));
escape(Int) when is_integer(Int) ->
    escape(integer_to_list(Int));
escape(String) ->
    escape(String, []).

escape([], Acc) ->
    lists:reverse(Acc);
escape([C | Rest], Acc) when ?QS_SAFE(C) ->
    escape(Rest, [C | Acc]);
escape([$\s | Rest], Acc) ->
    escape(Rest, [$+ | Acc]);
escape([C | Rest], Acc) ->
    <<Hi:4, Lo:4>> = <<C>>,
    escape(Rest, [hexdigit(Lo), hexdigit(Hi), ?PERCENT | Acc]).

revjoin([], _Separator, Acc) ->
    Acc;
revjoin([S | Rest], Separator, []) ->
    revjoin(Rest, Separator, [S]);
revjoin([S | Rest], Separator, Acc) ->
    revjoin(Rest, Separator, [S, Separator | Acc]).  

encode(Props) ->
    RevPairs = lists:foldl(fun ({K, V}, Acc) ->
				   [[escape(K), $=, escape(V)] | Acc]
			   end, [], Props),
    lists:flatten(revjoin(RevPairs, $&, [])).
